OPcache

一、引言:重要概念

OPcache(Optimized PHP Code Accelerator)

是PHP内置的字节码缓存扩展,通过缓存PHP脚本的编译结果(字节码),显著减少重复解析和编译的开销,从而提升PHP应用的执行效率。它是现代PHP性能优化的核心组件之一。

解释型、编译型语言

解释型语言(Interpreted Language)

定义

解释型语言的程序在运行时由 解释器(Interpreter) 逐行读取、解析并执行,不生成独立的机器码文件,而是直接翻译并执行源代码。
例如:Python、JavaScript、Ruby、PHP。

核心特点

典型例子

编译型语言(Compiled Language)

定义

编译型语言的程序在运行前必须通过 编译器(Compiler) 将源代码转换为 机器码(二进制文件),生成独立的可执行文件(如 .exe.out)。
例如:C、C++、Go、Rust。

核心特点

典型例子

总结

特性 解释型语言 编译型语言
执行方式 运行时逐行解释执行 先编译为机器码,后直接运行
可执行文件 无(依赖解释器) 有(独立二进制文件)
跨平台性 高(依赖解释器) 低(依赖操作系统/架构)
执行速度 较慢(需逐行翻译) 快(直接机器码执行)
内存管理 自动(垃圾回收) 手动或自动(如 Go 的垃圾回收)
开发效率 高(无需编译,修改即运行) 低(需频繁编译)
典型语言 Python、JavaScript、PHP C、C++、Go、Rust、JAVA

解释器、编译器、混合模式

解释器(Interpreter)

定义:

逐条将源代码翻译成机器指令并立即执行,不生成中间或最终的机器码文件

流程:

  1. 解释阶段:逐行读取源代码,实时翻译成可执行指令。
  2. 执行阶段:翻译后的指令直接由计算机执行。

特点:

典型解释器

编译器(Compiler)

定义

将源程序的每一条语句编译成机器语言代码,并保存为二进制文件(可执行文件或目标代码)。

流程

  1. 编译阶段:将源代码转换为与硬件直接兼容的机器码。
  2. 执行阶段:运行时计算机直接加载二进制文件,无需再次翻译。

特点

典型编译器

混合模式:现代语言的突破

随着技术发展,许多语言结合了编译和解释的特性:

  1. JIT(即时编译)
    • 如 Java 的 JVM、JavaScript 的 V8 引擎,先将代码编译为中间字节码,再在运行时动态编译为机器码(如热点代码优化)。
  2. 预编译中间代码
    • 如 C# 的 CLR、Java 的 JVM,将代码编译为平台无关的字节码,再由虚拟机解释或 JIT 编译执行。

总结

特性 编译器 解释器 混合模式(如 JIT)
定义 将源代码转换为机器码,生成可执行文件 逐行翻译并执行,不生成机器码文件 先编译为中间代码,运行时动态编译优化代码
执行方式 一次性编译,之后直接执行机器码 逐行解释执行,无中间文件生成 预编译为中间代码(如字节码),运行时优化执行
执行速度 快,机器码直接执行 慢,逐行翻译开销大 初始慢,但通过JIT编译高频代码后接近编译速度
依赖环境 可执行文件独立于编译器运行 需要解释器存在才能运行 需要虚拟机(如JVM、CLR)或运行时环境
错误调试 编译期报错,可能难以定位复杂错误 运行时逐行报错,调试更直观 运行时错误调试,结合编译期和运行期反馈
跨平台性 生成特定平台代码(需重新编译) 通过解释器实现跨平台(需对应解释器) 中间代码(如字节码)跨平台,依赖虚拟机
生成的文件类型 可执行文件(如 .exe, .out) 无独立可执行文件 中间代码文件(如 Java 的 .class)
典型例子 GCC(C/C++)、Clang、javac(Java) Python解释器、JavaScript V8(解释模式)、PHP Zend引擎 Java JVM、C# CLR、JavaScript V8(JIT模式)

二、基本原理

1. PHP脚本的执行流程

2. OPcache的优化

echo "Hello World!";

3. OPcache工作流程示意图

⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'

Excalidraw Data

Text Elements

请求PHP脚本
OPcache检查共享内存
直接执行字节码
返回结果
文件
修改?
重新编译
更新缓存
执行字节码
解析并编译
缓存字节码


缓存持续有效
缓存何时失效?
缓存相应条目失效
清空所有缓存
文件再次被请求时
重新编译并缓存
文件更新或过期
手动重置缓存
开始
结束
缓存
命中?

三、核心机制

1. 字节码缓存内容

OPcache 缓存的内容

预编译的字节码(Opcode)

函数和类定义

class MyClass {
  public function sayHello() {
	  return "Hello";
  }
}
// 编译后,MyClass的Opcode被缓存。

文件路径和依赖关系

require 'config.php'; // 缓存路径及config.php的mtime。

Interned Strings(内部化字符串)

OPArray 结构

OPcache 不缓存的内容

OPcache 的设计仅针对 PHP 脚本的编译结果,以下内容不在其缓存范围内:

运行时数据

未被包含的文件

动态生成的代码(如 eval() 的结果)

eval("echo 'Hello World';"); // 动态生成的代码不会被缓存。

注释(可配置)

2. 共享内存管理

3. 互斥锁(Mutex)机制

4. 缓存更新与失效

四、性能提升点

  1. 减少解析和编译开销:直接执行缓存的字节码,降低CPU和内存消耗。
  2. 代码优化:消除无用操作(如重复计算),合并重复代码。
  3. 共享内存复用:多PHP-FPM进程共享字节码,降低内存占用。
    示例

五、配置与调优

1. 关键参数

参数 作用 建议值
opcache.enable 启用OPcache(1启用,0禁用) 生产环境设为1
opcache.memory_consumption 共享内存总大小(MB) 根据内存调整,如256或更高
opcache.max_accelerated_files 允许缓存的最大文件数 Laravel项目设为20000
opcache.revalidate_freq 文件修改检查频率(秒) 生产环境设为60,开发环境设为0
opcache.validate_timestamps 是否检查文件时间戳(1启用,0禁用) 生产环境设为0以避免频繁检查

2. 注意事项

[opcache]
opcache.enable=0
opcache.validate_timestamps=1

六、适用场景与限制

1. 适用场景

2. 重要限制

七、与其他技术的对比

类型 OPcache 文件内容缓存(如Redis)
缓存内容 PHP字节码 文件内容(JSON、配置文件等)
核心作用 减少PHP解析/编译开销 减少磁盘I/O,加速文件读取
配置方式 内置PHP配置参数 需手动编码实现缓存逻辑
适用场景 PHP脚本执行加速 频繁读取的静态文件或配置

八、实际应用与验证

1. 验证OPcache效果

1. 启用OPcache

php.ini中设置opcache.enable=1,重启PHP-FPM。

2. 查看 OPcache 配置配置信息

php -i | grep opcache

3. 监控状态

脚本监控

通过opcache_get_status()查看关键指标

$opcache_status = opcache_get_status();
echo json_encode($opcache_status);

理想状态:命中率应超过 90%,否则需调整配置(如增加 memory_consumptionmax_accelerated_files)。

计算 max_accelerated_files 的数量

OPcache 缓存的最大文件数量 (根据应用调整,确保足够大以容纳所有 PHP 文件)

find . -type f -name "*.php" | wc -l

图形化监控「OPcache GUI」

安装
# 克隆代码
git clone [email protected]:PeeHaa/OpCacheGUI.git
# 切换到根目录并拷贝配置文件
cd OpCacheGUI && cp config.sample.php config.php
# 添加nginx配置
server {
    listen       90;
    server_name  _;
    root   /www/localhost/OpCacheGUI/public;
    index  index.php index.html index.htm;

    location / {
          try_files $uri $uri/ /index.php?$query_string;
    }
    access_log /dev/null;
    error_log  /var/log/nginx/opcache.gui2.error.log  warn;
    location ~ \.php$ {
          fastcgi_pass   php80:9000;
          fastcgi_index  index.php;
          include        fastcgi_params;
          fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    }
}
使用步骤:
  1. 访问 http://10.0.0.6:90,查看缓存命中率、内存使用、已缓存文件列表等。
{
    "opcache_enabled": true,            // OPcache 是否启用 (true = 启用, false = 禁用)
    "cache_full": false,                // OPcache 缓存是否已满 (true = 已满, false = 未满)
    "restart_pending": false,           // 是否有重启请求等待执行 (true = 等待中, false = 否)
    "restart_in_progress": false,      // 是否正在进行重启 (true = 进行中, false = 否)
    "memory_usage": {
        "used_memory": 23648032,        // 已使用的内存 (字节)
        "free_memory": 110563008,       // 可用内存 (字节)
        "wasted_memory": 6688,          // 浪费的内存 (字节)
        "current_wasted_percentage": 0.004982948303222656 // 当前内存浪费百分比
    },
    "interned_strings_usage": {
        "buffer_size": 6290992,        // 内部字符串缓冲区大小 (字节)
        "used_memory": 3930744,        // 已使用的内部字符串内存 (字节)
        "free_memory": 2360248,        // 可用的内部字符串内存 (字节)
        "number_of_strings": 57142     // 内部字符串数量
    },
    "opcache_statistics": {
        "num_cached_scripts": 844,      // 已缓存的脚本数量
        "num_cached_keys": 1608,        // 已缓存的键数量
        "max_cached_keys": 16229,       // 最大缓存键数量
        "hits": 423611,                // 缓存命中次数
        "start_time": 1744158638,       // OPcache 启动时间 (Unix 时间戳)
        "last_restart_time": 0,         // 上次重启时间 (Unix 时间戳, 0 表示未重启)
        "oom_restarts": 0,              // 内存不足重启次数
        "hash_restarts": 0,             // 哈希冲突重启次数
        "manual_restarts": 0,           // 手动重启次数
        "misses": 855,                  // 缓存未命中次数
        "blacklist_misses": 0,          // 黑名单未命中次数
        "blacklist_miss_ratio": 0,      // 黑名单未命中率
        "opcache_hit_rate": 99.79857043909288 // OPcache 命中率 (%)
    }
}

2. 典型配置示例(生产环境)

[opcache]
; 启用 OPcache 扩展 (必须启用) (1 = 启用, 0 = 禁用)
opcache.enable=1
; CLI 模式也启用 Opcache (推荐)
opcache.enable_cli=1
; OPcache 使用的共享内存大小,单位为 MB (根据应用调整,建议 256MB 起步)
opcache.memory_consumption=256
; OPcache 缓存的最大文件数量 (根据应用调整,确保足够大以容纳所有 PHP 文件)
opcache.max_accelerated_files=20000
; 原理:当 validate_timestamps=1 时生效,定义检查文件更新的时间间隔(秒)。
; =0:每个请求都检查文件更新(性能最差,仅适合开发环境)。
; =N(如 60):每 N 秒检查一次,期间忽略文件修改
opcache.revalidate_freq=2
; 原理:控制 OPcache 是否检查 PHP 文件的修改时间(mtime)。
; =1(启用):定期检查文件是否更新,若更新则重新编译缓存。
; =0(禁用):完全跳过文件更新检查,缓存永不自动失效(需手动重置)
opcache.validate_timestamps=0
; 用于存储内部字符串的内存大小「避免存储重复的字符到内存引起的浪费」,单位为 MB。(根据应用调整,如果应用大量重复字符串操作,可以增加此值)
; interned strings 机制会创建一个特殊的共享内存池(Interned Strings Buffer)。当 PHP 遇到一个字符串时,它会首先检查这个字符串是否已经在内存池中。
; 如果存在,它会直接创建一个指向该字符串在内存池中地址的引用。
; 如果不存在,它会将这个字符串添加到内存池中,然后创建一个引用。
opcache.interned_strings_buffer=32
; 优化内存分配 (推荐启用)
opcache.optimization_level=0x7FFFBFFF
; 禁用文件统计,提高性能 (推荐启用)
opcache.use_cwd=1
; opcache.error_log=/var/log/php-fpm-opcache.log
; 含义:指定 Opcache 扩展自身的错误日志文件路径。
; 推荐:生产环境推荐开启并设置一个绝对路径。
; 理由:Opcache 扩展在运行时可能会遇到错误或警告(例如内存不足、文件无法缓存等)。
;       将这些错误记录到单独的日志文件,有助于监控 Opcache 的健康状况,及时发现并解决问题,
;       确保 Opcache 正常工作,从而发挥其性能优化作用。
;       确保日志文件路径存在,且 PHP-FPM 运行用户(如 www-data)对该路径有写入权限。
opcache.error_log=/var/log/php-fpm-opcache.log

; opcache.log_verbosity_level=1
; 含义:设置 Opcache 日志的详细程度。
;       0:不记录任何 Opcache 错误。
;       1:记录错误(默认值)。
;       2:记录警告。
;       3:记录信息(调试信息)。
;       4:记录调试信息。
; 推荐:生产环境推荐设置为 1 或 2。您这里设置为 1 是合适的。
; 理由:设置为 1 (默认) 足够记录 Opcache 的关键错误,有助于发现配置问题或运行时异常。
;       设置为 2 可以记录警告,提供更多信息,但可能增加日志量。
;       不建议在生产环境设置为 3 或 4,因为会产生大量调试信息,迅速填满日志文件,影响性能。
opcache.log_verbosity_level=1

; opcache.max_wasted_percentage=5
; 含义:当 Opcache 共享内存中“浪费”的内存(例如,由于文件更新导致旧缓存失效,但内存未被立即回收)达到此百分比时,
;       Opcache 会尝试重启自身(如果配置允许),以回收这些浪费的内存。
; 推荐:生产环境推荐开启并保持默认值 5% 或根据实际情况微调。您这里设置为 5 是合适的。
; 理由:防止 Opcache 内存碎片化严重,导致有效缓存空间不足。当浪费的内存达到阈值时,
;       Opcache 会尝试清理,确保缓存效率。5% 是一个合理的平衡点,既能避免频繁重启,又能有效管理内存。
opcache.max_wasted_percentage=5

; opcache.validate_permission=1
; 含义:当 Opcache 检查脚本文件的时间戳时,是否也同时检查文件的权限。
; 推荐:生产环境通常推荐设置为 0 (关闭)。您这里设置为 1 是不推荐的。
; 理由:
;   设置为 1 (开启):Opcache 会在每次文件时间戳验证时,额外检查文件的权限。这会增加文件系统 I/O 开销,
;                    对性能有轻微负面影响。在生产环境中,文件权限通常是固定的,并且由部署流程保证。
;   设置为 0 (关闭,推荐):Opcache 不会检查文件权限,只检查时间戳。这可以减少不必要的 I/O 操作,
;                    从而提高性能。前提是您已经通过文件系统权限管理确保了 PHP 脚本文件的正确访问权限。
opcache.validate_permission=0

; 启用并设置二级缓存目录。
; 当共享内存已满、服务器重启或共享内存重置时,它应该能提高性能。
; 默认的 "" 会禁用基于文件的缓存。
; 开启文件缓存作为二级缓存可以在某些情况下提供容错和启动加速。
opcache.file_cache=/tmp/opcache_cache

; 启用或禁用共享内存中的操作码缓存。
; 如果启用了 opcache.file_cache,并且你希望仅从文件缓存加载,可以设置为 1。
;opcache.file_cache_only=0
; 当从文件缓存加载脚本时,启用或禁用校验和验证。
; 开启可以确保文件完整性,但会略微增加开销。在生产环境通常保持开启。
opcache.file_cache_consistency_checks=1

;关闭文档注释缓存(节省内存)
opcache.save_comments=0

; 启用大页内存(需OS支持)
opcache.huge_code_pages=1

; +----------------------------------------------------------------------  
; | JIT   
; +----------------------------------------------------------------------
; 保持tracing模式(最佳选择)
opcache.jit = tracing
; 关键!启用JIT(建议100-200MB)
opcache.jit_buffer_size = 160M
; 提升多态调用处理能力(适应依赖注入)
opcache.jit_max_polymorphic_calls = 8
; 增加递归优化深度(适应路由解析)
opcache.jit_max_recursive_calls = 5
; 增加根跟踪数(复杂框架需要)
opcache.jit_max_root_traces = 1024
; 提升热点函数优化阈值
opcache.jit_hot_func = 192
; 其他性能参数
; 保持开启(提升超全局变量性能)
auto_globals_jit = On
; 保持开启(加速正则匹配)
pcre.jit = On
; 提升内联优化能力
; 增加循环展开优化
opcache.jit_hot_loop = 128
; 提升返回点优化
opcache.jit_hot_return = 16
; 增加跟踪能力
; 适应复杂业务逻辑
opcache.jit_max_side_traces = 128
; 允许更长代码路径优化
opcache.jit_max_trace_length = 1024

opcache.interned_strings_buffer 缓冲区溢出的性能影响

指标 正常状态 溢出状态 (当前) 性能损耗
内存占用 减少 20-30% 增加 15-25% ⬆️ 45-55%
请求处理时间 稳定低延迟 波动增大 (+10-30ms) ⬆️ 15%
CPU 使用率 正常水平 峰值增加 8-12% ⬆️ 10%
GC(Garbage Collection) 低频触发 频繁触发 ⬆️ 300%

3.对比测试结果

指标 开启前 开启后 变化率 优化效果
平均值(ms) 132 24 -82.6% 响应时间显著降低
中位数(ms) 89 18 -79.8% 多数请求速度大幅提升
90% 百分位(ms) 291 21 -92.8% 长尾请求优化显著
95% 百分位(ms) 362 27 -92.5% 极端情况响应时间减少
99% 百分位(ms) 384 190 -50.5% 极端情况仍有优化空间
最大值(ms) 386 219 -43.3% 最慢请求时间缩短
吞吐量(#/sec) 10.17 10.12 -0.5% 几乎无变化
接收数据速率(KB/s) 344.32 342.40 -0.6% 无显著变化
发送数据速率(KB/s) 33.85 33.67 -0.5% 无显著变化

九、常见误区与解决方案

1. 误区:OPcache能缓存所有文件

2. 误区:OPcache配置越大越好

十、总结

OPcache通过字节码缓存、共享内存复用和代码优化,显著提升PHP应用的执行效率。合理配置参数(如内存大小、文件数量限制)并结合其他缓存技术(如Redis),可实现更全面的性能优化。在生产环境中,需禁用Xdebug并监控缓存命中率,以确保OPcache发挥最佳效果。